import io.fabric8.kubernetes.client.dsl.ExecWatch;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.charset.StandardCharsets;


@Data
@Slf4j
public class ShellTerminal {

    private static final String EXIT_CODE = "exit";
    private static final String CLOSED = "closed";

    private String id;
    private String remoteAddress;
    private ExecWatch execWatch;
    private ChannelHandlerContext ctx;

    public ShellTerminal(String id, ExecWatch opsExecWatch, ChannelHandlerContext ctx) {
        this.id = id;
        this.execWatch = opsExecWatch;
        this.ctx = ctx;
        initListenThread(id, execWatch, ctx);
    }

    public void input(String inputText) throws IOException {
        if (EXIT_CODE.equals(inputText)) {
            closeExecWatch();
            this.ctx.close();
            return;
        }
        inputText += "\n";
        OutputStream stdin = execWatch.getInput();
        if (stdin != null) {
            stdin.write(inputText.getBytes(StandardCharsets.UTF_8));
            stdin.flush();
        }
    }


    public void closeExecWatch() {
        if (execWatch == null) {
            log.info("[{}] execWatch is null {}", id, remoteAddress);
            return;
        }
        try {
            OutputStream stdin = execWatch.getInput();
            InputStream stdout = execWatch.getOutput();
            InputStream stderror = execWatch.getError();
            if (stdin != null) {
                stdin.close();
            }
            if (stdout != null) {
                stdout.close();
            }
            if (stderror != null) {
                stderror.close();
            }
        } catch (Exception ex) {
            log.error("[{}] close execWatch error {}:{}", id, remoteAddress, ex.getMessage(), ex);
        }
    }


    public void initListenThread(String id, ExecWatch execWatch, ChannelHandlerContext ctx) {
        // 创建一个线程用于持续读取输出并打印
        Thread outputThread = new ShellOutputThread(id, execWatch, ctx);
        outputThread.start();
    }


    public static class ShellOutputThread extends Thread {

        private final String id;
        private final ChannelHandlerContext ctx;
        private final ExecWatch execWatch;

        public ShellOutputThread(String id, ExecWatch execWatch, ChannelHandlerContext ctx) {
            this.id = id;
            this.execWatch = execWatch;
            this.ctx = ctx;
        }

        @Override
        public void run() {
            try {
                InputStream output = execWatch.getOutput();
                BufferedReader stdout = new BufferedReader(new InputStreamReader(output));
                String line;
                while ((line = stdout.readLine()) != null) {
                    System.out.println(line + "\n");
                    ctx.channel().writeAndFlush(new TextWebSocketFrame(line + "\n"));
                }
            } catch (Exception e) {
                if ((e instanceof IOException) && CLOSED.equals(e.getMessage())) {
                    log.error("[" + id + "] shell output stream closed");
                } else {
                    log.error("[" + id + "] shell output read error:{}", e.getMessage(), e);
                }
            }
        }
    }
}

